package com.cmpp.client.util;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import com.cmpp.client.domain.*;

/**
 * 基础通信类，创建socket,连接网关，启动接受线程
 * @author zhuangyzh
 *
 */

public class CMPPConnection {
    private Socket socket;    
    static InputStream inStream = null;
    static OutputStream outStream = null;
    public static  DataInputStream in;
    public static  DataOutputStream out;             
    private final static ConcurrentHashMap<String, byte[]> recMsgMap = new ConcurrentHashMap<String, byte[]>();    
    private static CMPPConnection CMPPConnection;
    public CMPPConnection(){
        System.out.println("加载配置文件");
        new CMPPConfig();        
        System.out.println("创建SOCKET连接");
        getSocketInstance();
    }
    
    
    /**
     * 单态模式 
     */
    public static CMPPConnection getInstance() {
        if(CMPPConnection==null) {
            synchronized(CMPPConnection.class) {
                if(CMPPConnection==null) {
                    CMPPConnection = new CMPPConnection();
                    return CMPPConnection;
                }
            }
        }
        return CMPPConnection;
    }
    
    
    /**
     * 建立socket连接 
     */
    public  Socket getSocketInstance() {

        System.out.println("开始建立socket链接");
        try {
        socket = new Socket(CMPPConfig.ServerIp,CMPPConfig.ServerPort);
        synchronized (this) {
            in=new DataInputStream(socket.getInputStream());
            out=new DataOutputStream(socket.getOutputStream());
            socket.setTcpNoDelay(true);//数据不作缓冲，立即发送
            socket.setSoLinger(true, 0);//socket关闭时，立即释放资源
            socket.setKeepAlive(true);
            socket.setTrafficClass(0x04|0x10);//高可靠性和最小延迟传输                       
            System.out.println("建立接收线程");
            Thread receiveThread = new Thread(new CMPPRec(out,in));
            receiveThread.start();
        }
        }catch (Exception e) {          
            System.out.println("Socket链接短信网关失败："+e.getMessage());
        }
        return socket;
    }
    
    /**
     * 注册到网关
     * @return
     */
    public   boolean connectISMG(){
        System.out.println("注册请求");
        CMPPConnect connect=new CMPPConnect();
        connect.setTotal_Length(12+6+16+1+4);//消息总长度，级总字节数:4+4+4(消息头)+6+16+1+4(消息主体)
        connect.setCommand_Id(CMPPDefine.CMPP_CONNECT);//标识创建连接
        connect.setSequence_Id(CMPPUtils.getSequence());//序列，由我们指定
        connect.setSourceAddr(CMPPConfig.CompanyCode);//我们的企业代码
        connect.setAuthenticatorSource(CMPPUtils.md5(CMPPConfig.CompanyCode,CMPPConfig.Pwd));//md5(企业代码+密匙+时间戳)
        connect.setTimestamp(Integer.parseInt(CMPPUtils.getTimestamp()));//时间戳(MMDDHHMMSS)
        connect.setVersion((byte)0x30);//版本号 高4bit为3，低4位为0
        CMPPSend(connect.toByteArry());
        System.out.println("注册请求完毕");
        return true;
        }

    /**
     * 发送数据方法
     * @param requestMsg
     * @return
     * @throws SocketTimeoutException
     * @throws IOException
     */
    public  void CMPPSend(byte[] Msg) {
        System.out.println("===CMPPSend  输出字节流:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====："+CMPPUtils.bytesToHexString(Msg));
        String msgNo = CMPPUtils.bytesToHexString(CMPPUtils.getMsgBytes(Msg,8,12));//读取流水号        
        recMsgMap.put(msgNo, Msg);       
        try {
            outStream = socket.getOutputStream();       
            outStream.write(Msg);
            outStream.flush();
        } catch (IOException e) {
            System.out.println("===CMPPSend  输出字节流:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：Socket输出流打开失败");            
        }       
    }
    
    /**
     * socket 关闭方法
     * @throws IOException
     * @author zhuangyzh
     */   
    public void finalize() {
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 普通短信发送方法
     * @throws IOException
     * @author zhuangyzh
     */       
    public  void sendShortMsg(String msg, String number) {
        try {
            int seq = CMPPDefine.getSequence();
            CMPPSubmit submit = new CMPPSubmit();
            submit.setTotal_Length(12 + 8 + 1 + 1 + 1 + 1 + 10 + 1 + 32 + 1 + 1
                    + 1 + 1 + 6 + 2 + 6 + 17 + 17 + 21 + 1 + 32 + 1 + 1
                    + msg.length() * 2 + 20);
            submit.setCommand_Id(CMPPDefine.CMPP_SUBMIT);
            submit.setSequence_Id(seq);
            submit.setPkTotal((byte) 0x01);
            submit.setPkNumber((byte) 0x01);
            submit.setRegisteredDelivery((byte) 0x00);
            submit.setMsgLevel((byte) 0x01);
            submit.setFeeUserType((byte) 0x02);
            submit.setFeeTerminalId("");
            submit.setFeeTerminalType((byte) 0x00);
            submit.setTpPId((byte) 0x00);
            submit.setTpUdhi((byte) 0x00);
            submit.setMsgFmt((byte) 0x0F);
            submit.setMsgSrc(CMPPConfig.CompanyCode);
            submit.setSrcId(CMPPConfig.ServiceCode);
            submit.setDestTerminalId(number);
            submit.setMsgLength((byte) (msg.length() * 2));
            submit.setMsgContent(msg.getBytes("gb2312"));
            submit.setServiceId(CMPPConfig.BusinessCode);
            CMPPSend(submit.toByteArry());
            
            System.out.println("向手机号码："+number+"下发短短信，序号为:"+seq);
       
        } catch (Exception e) {
            try {
                out.close();
            } catch (IOException e1) {
                out = null;
            }
            System.out.println("发送短短信" + e.getMessage());
            //return -1;
        }
    }
   
    /**
     * 消息接收处理线程，在socket建立成功后即启动接收线程
     * @param 
     * @return
     * @throws SocketTimeoutException
     * @throws IOException
     * @throws InterruptedException
     * @author zhuangyzh
     */
    private static class CMPPRec  implements Runnable {        
        public static boolean runnabled = true;
        private DataOutputStream out;
        private DataInputStream in;
        public CMPPRec(DataOutputStream out,DataInputStream in) {
            this.out=out;
            this.in=in; 
        }
        
        public void run() {
            int i = 0;
            int j = 0;
            
            while (runnabled) {
                try{
                    
                    byte[] returnData=getInData();
                    System.out.println("===CmppRec  读取到字节流:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====："+CMPPUtils.bytesToHexString(returnData));
                    
                    if(returnData!=null && returnData.length>=8){
                        CMPPHeader head=new CMPPHeader(returnData);                   
                        
                        switch(head.getCommand_Id()){
                            case CMPPDefine.CMPP_CONNECT_RESP:
                                CMPPConnectResp connectResp=new CMPPConnectResp(returnData);
                                System.out.println("===CmppRec："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：链接短信网关,返回状态:"+connectResp.getStatusStr()+"      序列号："+connectResp.getSequence_Id());
                               
                                String recmsgNo = CMPPUtils.bytesToHexString(CMPPUtils.getMsgBytes(returnData,4,8));//读取流水号
                                
                                byte[] msgmsg = recMsgMap.get(recmsgNo);
                                
                                if(msgmsg ==null) {
                                    System.out.println("序号可能已被注销！响应消息丢弃"+recmsgNo);
                                    recMsgMap.remove(recmsgNo);                                    
                                }else {
                                //System.out.println("====请求连接返回值md5散列码：" + MsgUtils.bytes2hex(connectResp.getAuthenticatorISMG()));
                                if(connectResp.getStatus()==0){
                                    System.out.println("注册成功");
                                }else {
                                    System.out.println("注册失败返回值"+connectResp.getStatus());
                                    this.finalize();
                                    System.exit(0);
                                }
                                }
                                break;
                            case CMPPDefine.CMPP_ACTIVE_TEST:
                                System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：接收到短信网关连接检查请求---");
                                
                                byte[] seqs = new byte[4];
                                System.arraycopy(returnData, 4, seqs, 0, 4);
                                
                                CMPPActiveTestResp ActiveTestResp = new CMPPActiveTestResp();
                                ActiveTestResp.setTotal_Length(12+1);
                                ActiveTestResp.setCommand_Id(CMPPDefine.CMPP_ACTIVE_TEST_RESP);
                                ActiveTestResp.setSeq_Id(seqs);
                                
                                String fss = CMPPUtils.bytesToHexString(ActiveTestResp.toByteArry());
                                System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：CMPP_ACTIVE_TEST_RESP 回复网关："+fss);
                                sendMsg(ActiveTestResp.toByteArry());//进行回复
                                
                                System.out.println("---CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：CMPP_ACTIVE_TEST_RESP 回复网关完毕...");
                                
                                break;
                            case CMPPDefine.CMPP_ACTIVE_TEST_RESP:
                                CMPPActiveTestResp activeResp=new CMPPActiveTestResp(returnData);
                                System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：发送给短信网关进行连接检查的返回---"+" 序列号："+activeResp.getSequence_Id());
                                
                                break;
                            case CMPPDefine.CMPP_SUBMIT_RESP:
                                CMPPSubmitResp submitResp=new CMPPSubmitResp(returnData);
                                System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：向用户下发短信，返回状态:"+submitResp.getResult()+"      序列号："+submitResp.getSequence_Id());
                                System.out.println(submitResp.getResult());
                                if(submitResp.getResult()==0){
                                    //b = true;
                                    System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：msgId:"+submitResp.getMsgIdStr());
                                }
                                //发送处理
                                System.out.println("===CmppReceive_1---进入处理逻辑(下行)---");
                                break;
                            case CMPPDefine.CMPP_TERMINATE:
                                System.out.println("ismg请求断开连接");
                                break;
                                
                            case CMPPDefine.CMPP_DELIVER:
                                byte[] b = new byte[8];
                                System.arraycopy(returnData, 8, b, 0, 8);
                                
                                byte[] seq = new byte[4];
                                System.arraycopy(returnData, 4, seq, 0, 4);
                                
                                CMPPDeliver msgDeliver=new CMPPDeliver(returnData);
                                if(msgDeliver.getResult()==0){
                                    //System.out.println("===CmppReceive："+sdate+"：CMPP_DELIVER接收短信    序列号："+head.getSequenceId()+"消息内容："+msgDeliver.getMsg_Content()+"，手机号码是："+msgDeliver.getSrc_terminal_Id());
                                    System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：CMPP_DELIVER接收短信    序列号："+head.getSequence_Id()+"，是否上行短信："+(msgDeliver.getRegistered_Delivery()==0?"是,消息内容："+msgDeliver.getMsg_Content()+"，手机号码是："+msgDeliver.getSrc_terminal_Id():"不是，是状态报告。"));
                                    
                                    CMPPDeliverResp msgDeliverResp=new CMPPDeliverResp();
                                    msgDeliverResp.setTotal_Length(12+8+4);
                                    msgDeliverResp.setCommand_Id(CMPPDefine.CMPP_DELIVER_RESP);
                                    msgDeliverResp.setSeq_Id(seq);
                                    msgDeliverResp.setMsg_Id(b);
                                    msgDeliverResp.setResult(msgDeliver.getResult());
                                    
                                    String fs = CMPPUtils.bytesToHexString(msgDeliverResp.toByteArry());
                                    System.out.println("---CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：CMPP_DELIVER_RESP 回复网关："+fs);
                                    sendMsg(msgDeliverResp.toByteArry());//进行回复
//                                  CmppSender sender=new CmppSender(out,in,msgDeliverResp.toByteArry());
//                                  boolean a = sender.start();
                                    
                                    //接收处理
                                    System.out.println("---进入处理逻辑(上行)---");
                                    
                                    System.out.println("---CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：CMPP_DELIVER_RESP 回复网关完毕...");
                                    
                                }else{
                                    System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：CMPP_DELIVER 序列号："+head.getSequence_Id());
                                }
                                
                                break;
                            default:
                                System.out.println("===CmppReceive_1："+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：无法解析IMSP返回的包结构：包长度为"+head.getTotal_Length());
                                break;
                        }
                                            
                        Thread.sleep(1000);//休息1秒，继续读取
                        
                        i = 0;
                    }else{
                        System.out.println("======"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：没有读取到数据,或长度不正确，休息10秒====i:"+i);
                        Thread.sleep(10000);
                        i++;
                        if(i==15){
                            i = 0;
                            //System.out.println("======"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"=====：开始发送链路检查包");
                            //activityTestISMG();//发送链路检查(每3分钟发送一次)
                        }
                    }
                    j = 0;
                }catch(SocketException e){
                    System.out.println("*******************************"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"：短信接收socket链接出现异常*************************************");
                    e.printStackTrace();
                    j++;
                    if(j>=3){
                        runnabled = false;
                    }else{
                        System.out.println("*******************************"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"：短信接收重新建立socket链接*************************************");
                        new CMPPConnection();
                    }
                }catch(InterruptedException e){
                    System.out.println("*******************************"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"：短信接收线程出现异常*************************************");
                    j++;
                    if(j>=3){
                        runnabled = false;
                    }
                }catch(IOException e){
                    System.out.println("*******************************"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"：短信接收发送数据流出现异常*************************************");
                    j++;
                    if(j>=3){
                        runnabled = false;
                    }
                } catch (Throwable e) {
                   
                    e.printStackTrace();
                }
            }
        }
        
       
        
        private  byte[] getInData() throws IOException{
             try{
                 //读取四个输入字节并返回一个 int 值，其值是这条消息的总长度。
                 int len=in.readInt();
                 System.out.println("读取消息长度："+len);
                 
                 if(null!=in&&0!=len){
                   //继续读取剩余消息内容，并将其内容return回调用方法。
                   byte[] data=new byte[len-4];
                   in.read(data);
                   return data;
                 }else{
                     return null;
                 }
             }catch(NullPointerException ef){
                System.out.println("在本连结上接受字节消息:无流输入");         
                return null;
             }catch(EOFException eof){
                 System.out.println("在本连结上接受字节消息:"+eof.getMessage());           
                 return null;
             }
        }
        
        /**
           * 在本连结上发送已打包后的消息的字节
           * @param data:要发送消息的字节
           */
        private  boolean sendMsg(byte[] data) throws IOException{
           try{
              out.write(data);
              out.flush();
              System.out.println("---发送网关输出流完毕...");
              return true;
           }catch(NullPointerException ef){
              System.out.println("在本连结上发送已打包后的消息的字节:无字节输入");
           }
           return false;
        }        
    }
}
